//! Collect and compile SASS files to produce CSS stylesheets.

use glob::glob;
use std::fs::{copy, read_to_string, remove_file, File};
use std::io::Write;
use std::process::Command;

use crate::frontend::tools::execute_with_nvm;
use crate::util::{info, unwrap_or_exit, warn};

/// The name of the SASS file that imports all other SASS files
/// created in the modules.
static MODULES_FILE: &'static str = "static/css/modules.scss";

/// The SASS file assembling all other files.
static SASS_FILE: &'static str = "static/css/bootstrap-theme.scss";

/// The CSS bundle.
static CSS_FILE: &'static str = "static/css/style.css";
static CSS_FILE_HASHED: &'static str = "static/css/style.{}.css";
static CSS_HASH_FILE: &'static str = "static/css/.pgml-bundle";

/// Finds all the SASS files we have generated or the user has created.
static MODULES_GLOB: &'static str = "src/components/**/*.scss";

/// Finds old CSS bundles we created.
static OLD_BUNLDES_GLOB: &'static str = "static/css/style.*.css";

/// Sass compiler
static SASS_COMPILER: &'static str = "sass";

/// Find Sass files and register them with modules.scss.
fn assemble_modules() {
    // Assemble SCSS.
    let scss = unwrap_or_exit!(glob(MODULES_GLOB));

    let mut modules = unwrap_or_exit!(File::create(MODULES_FILE));

    unwrap_or_exit!(writeln!(
        &mut modules,
        "// This file is automatically generated."
    ));
    unwrap_or_exit!(writeln!(
        &mut modules,
        "// There is no need to edit it manually."
    ));
    unwrap_or_exit!(writeln!(&mut modules, ""));

    for stylesheet in scss {
        let stylesheet = unwrap_or_exit!(stylesheet);

        debug!("Adding '{}' to SCSS bundle", stylesheet.display());

        let line = format!(r#"@import "../../{}";"#, stylesheet.display());

        unwrap_or_exit!(writeln!(&mut modules, "{}", line));
    }

    info(&format!("written {}", MODULES_FILE));
}

/// Delete old bundles we may have created.
fn cleanup_old_bundles() {
    // Clean up old bundles
    for file in unwrap_or_exit!(glob(OLD_BUNLDES_GLOB)) {
        let file = unwrap_or_exit!(file);
        debug!("removing {}", file.display());
        unwrap_or_exit!(remove_file(file.clone()));
        warn(&format!("deleted {}", file.display()));
    }
}

/// Entrypoint.
pub fn bundle() {
    assemble_modules();
    cleanup_old_bundles();

    // Build Sass.
    info("bundling css with sass");
    unwrap_or_exit!(execute_with_nvm(
        Command::new(SASS_COMPILER).arg(SASS_FILE).arg(CSS_FILE),
    ));

    info(&format!("written {}", CSS_FILE));

    // Hash the bundle to bust all caches.
    let bundle = read_to_string(CSS_FILE).expect("failed to read bundle.css");
    let hash = format!("{:x}", md5::compute(bundle))
        .chars()
        .take(8)
        .collect::<String>();

    let hash_file = CSS_FILE_HASHED.replace("{}", &hash);

    unwrap_or_exit!(copy(CSS_FILE, &hash_file));
    info(&format!("written {}", hash_file));

    let mut hash_file = unwrap_or_exit!(File::create(CSS_HASH_FILE));
    unwrap_or_exit!(writeln!(&mut hash_file, "{}", hash));

    info(&format!("written {}", CSS_HASH_FILE));
}
